package com.komon.object.request;

import java.io.IOException;
import java.io.InputStream;

import com.komon.constant.HttpHeader;
import com.komon.constant.HttpMethod;
import com.komon.constant.header.EntityHeader;
import com.komon.constant.header.GeneralHeader;
import com.komon.constant.header.RequestHeader;
import com.komon.exception.StatusCodeExceptoin;
import com.komon.factory.TransferEncodingInputStreamFactory;
import com.komon.object.request.bodyIO.FixedLengthRequestInputStream;
import com.komon.object.request.bodyIO.HttpRequestBodyEncodingInputStream;
import com.komon.object.request.bodyIO.NullRequestInputStream;
import com.komon.util.CommonUtils;

/**
 * This Object is the main object used in Komon
 * 
 * @author Timon
 */
public class InternalHttpRequest {
    private SimpleHttpRequest simpleHttpRequest;
    // headers object are generated by headers Map
    private GeneralHeader generalHeader = new GeneralHeader();
    private EntityHeader entityHeader = new EntityHeader();
    private RequestHeader requestHeader = new RequestHeader();

    private String charsetEncoding;

    private final HttpRequestBodyEncodingInputStream bodyEncodingInputStream;

    public InternalHttpRequest(SimpleHttpRequest simpleHttpRequest)
            throws StatusCodeExceptoin {
        this.simpleHttpRequest = simpleHttpRequest;
        // TODO combine?
        HttpHeader
                .populateHeader(simpleHttpRequest.getHeaders(), generalHeader);
        HttpHeader.populateHeader(simpleHttpRequest.getHeaders(), entityHeader);
        HttpHeader
                .populateHeader(simpleHttpRequest.getHeaders(), requestHeader);

        bodyEncodingInputStream = getTransferEncodingInputStream();
        // TODO reader
    }

    public boolean isConnectionClosed() {
        return this.generalHeader.getConnection().equalsIgnoreCase("Close");
    }

    public void skipCurrentRequest() throws IOException {
        this.bodyEncodingInputStream.skipCurrentRequest();
    }

    /**
     * This method will create the inputStream per specific transfer_encodings
     * Note: this method will be called only once in construct
     */
    private HttpRequestBodyEncodingInputStream getTransferEncodingInputStream()
            throws StatusCodeExceptoin {
        InputStream socketInputStream = simpleHttpRequest
                .getSocketInputStream();
        if (!HttpMethod.willHaveMessageBody(simpleHttpRequest.getRequestLine()
                .getMethod())) {
            return new NullRequestInputStream(socketInputStream);
        }

        // If a Transfer-Encoding header field is present and has any value
        // other than "identity", then the transfer-length is defined by use of
        // the "chunked" transfer-coding,
        // unless the message is terminated by closing the connection.
        // If both transfer_encoding and content_length exists, ignore
        // contentLength
        // All transfer-coding values are case-insensitive
        String transfer_encoding = generalHeader.getTransfer_Encoding();
        if (!CommonUtils.isEmptyString(transfer_encoding)
                && !transfer_encoding.equalsIgnoreCase("identity")) {
            // If multiple encodings have been applied to an entity, the
            // transfer- codings MUST be listed in the order in which they were
            // applied
            // When the "chunked" transfer- coding is used, it MUST be the last
            // transfer-coding applied to the message-body
            String[] transfer_encodings = transfer_encoding.split(",");
            HttpRequestBodyEncodingInputStream bodyInputStream = null;

            for (int i = transfer_encodings.length - 1; i >= 0; i--) {
                bodyInputStream = TransferEncodingInputStreamFactory
                        .wrapInputStream(transfer_encoding, bodyInputStream);
            }
            return bodyInputStream;
        }
        // run here means no transfer-encodingm we try to check the
        // content-length
        String content_length = entityHeader.getContent_Length();
        if (!CommonUtils.isEmptyString(content_length)) {
            return new FixedLengthRequestInputStream(Integer
                    .valueOf(content_length), socketInputStream);
        }
        // if run here, we don't know how to parse the body beacuse we don't
        // know the message length
        // If a request contains a message-body and a Content-Length is not
        // given, the server SHOULD respond with 400 (bad request) if it cannot
        // determine the length of the message,
        // or with 411 (length required) if it wishes to insist on receiving a
        // valid Content-Length.
        throw new StatusCodeExceptoin(400);
    }

    public String getCharsetEncoding() {
        if (charsetEncoding == null) {
            String contentType = entityHeader.getContent_Type();
            if (contentType == null)
                return (null);
            int start = contentType.indexOf("charset=");
            if (start < 0)
                return (null);
            String encoding = contentType.substring(start + 8);
            int end = encoding.indexOf(';');
            if (end >= 0)
                encoding = encoding.substring(0, end);
            encoding = encoding.trim();
            if ((encoding.length() > 2) && (encoding.startsWith("\""))
                    && (encoding.endsWith("\"")))
                encoding = encoding.substring(1, encoding.length() - 1);
            charsetEncoding = encoding.trim();
        }
        return charsetEncoding;
    }

    public SimpleHttpRequest getSimpleHttpRequest() {
        return simpleHttpRequest;
    }
}
